package edu.drexel.jrexel2d.gamestate;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import edu.drexel.jrexel2d.animation.Sprite;
import edu.drexel.jrexel2d.animation.SpriteSheet;
import edu.drexel.jrexel2d.objects2D.*;
import edu.drexel.jrexel2d.physics.PhysicsEngine;

public class Level implements Serializable {
	private ArrayList<Object2D> objects;
	private transient HashMap<String, Sprite> sprites;
	private int counter;
	private ArrayList<Point2D> spawns;
	private ArrayList<Player> players;
	private transient Backdrop backdrop;
	private transient PhysicsEngine refPhysicsEngine;
	private long time;

	public Level(String name) {
		objects = new ArrayList<Object2D>();
		sprites = new HashMap<String, Sprite>();
		counter = 0;
		spawns = new ArrayList<Point2D>();
		players = new ArrayList<Player>();
		backdrop = new Backdrop(name);

		parseData(name);
	}

	private void parseData(String name) {
		try {
			BufferedReader br = new BufferedReader(new FileReader(
					"assets/levelData/" + name + ".txt"));
			String line;

			while ((line = br.readLine()) != null) {
				String token = line.trim();
				if (token.equals("objects"))
					loadObjects(br); // Load the various objects/enemies in the
										// level
				if (token.equals("players")) {
					loadPlayers(br);
				} // Loads spawnpoint locations for players
			}

			br.close();
		} catch (Exception e) {
			System.out.println("Error reading file: " + e);
		}
	}

	private void loadObjects(BufferedReader br) {
		try {
			String line;

			while (!((line = br.readLine()).trim()).equals("/objects")) {
				String[] data = (line.trim()).split(",");
				
				Object2D obj = loadSheets(data, true) ;
				
				obj.setLocation(Integer.parseInt(data[1]),
						Integer.parseInt(data[2]));
				
                if(!data[3].isEmpty() && data[3].equals("movable")){
                	obj.setMovable(true);
                }
                
                if(!data[4].isEmpty() && data[4].equals("pickable")){
                	obj.setPickable(true);
                }
                
				objects.add(obj);
			}
		} catch (Exception e) {
			System.out.println("Error loading objects: " + e);
		}
	}

	private Object2D makeObj(Sprite s, int i, int r, ArrayList<Point2D> ps, boolean pFlag) {
		Object2D o;
		String shape = s.getShape();

		if(!pFlag) {
			o = new Player(s, i, players.size()+1) ;
		}
		else if (shape.equals("rect")) {
			o = new Rectangle(s, i);
		}
		else if(shape.equals("circ")) {
			o = new Circle(s, i, r) ;
		}
		else if(shape.equals("poly")) {
			o = new Polygon(s, i, ps) ;
		}
		else {
			o = new Object2D(s, i);
		}
		counter++ ;

		return o;
	}

	private void loadPlayers(BufferedReader br) {
		try {
			String line;

			while (!((line = br.readLine()).trim()).equals("/players")) {
				String[] data = (line.trim()).split(",");
				
				spawns.add(new Point2D(Integer.parseInt(data[1]), Integer.parseInt(data[2])));
						
				spawnPlayer((Player) loadSheets(data, false)) ;

			}
		} catch (Exception e) {
			System.out.println("Error loading spawns: " + e);
			e.printStackTrace() ;
		}
	}
	
	private Object2D loadSheets(String[] data, boolean pFlag) {
		try {
			Sprite s = sprites.get(data[0]);
			int width = 16;
			int height = 16;
			ArrayList<Point2D> points = new ArrayList<Point2D>() ;
	
			//if (s == null) {
				BufferedReader br2 = new BufferedReader(new FileReader(
						"assets/sprites/" + data[0] + "/spriteData.txt"));
				String shape = (br2.readLine()).trim();
				
				if(s == null || shape.equals("poly")) {
					String line2;
					s = new Sprite(data[0], shape);
					
					while ((line2 = br2.readLine()) != null) {
						String sheetName = line2 + ""; // adding "" should
														// ensure it copies, and
														// doesn't make a
														// pointer
						Boolean bounce = Boolean.parseBoolean((br2.readLine())
								.trim());
						width = Integer.parseInt((br2.readLine()).trim());
						height = Integer.parseInt((br2.readLine()).trim());
		
						s.addSheet(new SpriteSheet(data[0], sheetName, bounce,
								width, height));
						
						// Pretty sure my way of handling polygons is awful, but whatever.
						// It's a little late to figure out a new way now.
						if(shape.equals("poly")) {
							line2 = br2.readLine().trim() ;
							String[] data2 = line2.split(";") ;
							for(String str: data2) {
								String[] coords = str.split(",") ;
								points.add(new Point2D(coords[0],coords[1])) ;
							}
						}
					}
				}
				sprites.put(data[0], s);
				br2.close();
			//}
			return makeObj(s, counter, width/2, points, pFlag);
			
		}
		catch(Exception e) {
			System.out.println("Error loading sheets: " + e) ;
			e.printStackTrace() ;
		}
		
		return null ;
	}

	public Object2D getObject(int i) {
		return objects.get(i);
	}

	public Object2D getObjectById(int id) {
		for (Object2D object : objects) {
			if (object.getId() == id) {
				return object;
			}
		}
		return null;
	}

	public Player getPlayerByObjectId(int id) {
		for (Player player : players) {
			if (player.getId() == id) {
				return player;
			}
		}
		return null;

	}

	public Player getPlayerByPlayerId(int id) {
		for (Player player : players) {
			if (player.getPlayerId() == id) {
				return player;
			}
		}
		return null;

	}

	public ArrayList<Object2D> getObjects() {
		return objects;
	}

	public ArrayList<Player> getPlayers() {
		return players;
	}

	public Backdrop getBackdrop() {
		return backdrop;
	}

	public void spawnPlayer(Player p) {
		p.setLocation(spawns.get(players.size()));
		players.add(p);
		objects.add(p);
	}

	public void setObjectPhysicsEngine(PhysicsEngine inPhysicsEngine) {

		refPhysicsEngine = inPhysicsEngine;

		for (int o = 0; o < objects.size(); ++o) {
			objects.get(o).setPhysicsEngine(inPhysicsEngine);

		}

	}

	public Map<Integer, Point2D> getObjectPointMap() {
		Map<Integer, Point2D> objectPointMap = new HashMap<Integer, Point2D>();
		for (Object2D object : objects) {
			objectPointMap.put(object.getId(), object.getLocation());
		}
		return objectPointMap;
	}

	// Updates the location of all Objects in Level
	public void mergeObjectsAndPlayers(Map<Integer, Point2D> updatedObjects) {
		Set<Entry<Integer, Point2D>> updatedObjectsSet = updatedObjects
				.entrySet();
		for (Entry<Integer, Point2D> updatedObject : updatedObjectsSet) {
			Object2D objectFromLevel = this.getObjectById(updatedObject
					.getKey());
			objectFromLevel.setLocation(updatedObject.getValue());
			if (objectFromLevel instanceof Player) {
				Player playerFromLevel = this.getPlayerByObjectId(updatedObject
						.getKey());
				playerFromLevel.setLocation(updatedObject.getValue());
			}
		}
	}

	// Updates the location of all Objects in Level except the local player
	public void mergeObjectsAndPlayersExceptCurrentPlayer(
			Map<Integer, Point2D> updatedObjects, int playerId) {
		Set<Entry<Integer, Point2D>> updatedObjectsSet = updatedObjects
				.entrySet();
		for (Entry<Integer, Point2D> updatedObject : updatedObjectsSet) {
			Object2D objectFromLevel = this.getObjectById(updatedObject
					.getKey());
			// update object in the object list
			if (!(objectFromLevel instanceof Player))
				objectFromLevel.setLocation(updatedObject.getValue());
			// update players in the object list and player list, except
			// currentPlayer
			if (objectFromLevel instanceof Player) {
				Player playerFromLevel = this.getPlayerByObjectId(updatedObject
						.getKey());
				if (playerFromLevel.getPlayerId() != playerId) {
					playerFromLevel.setLocation(updatedObject.getValue());
					objectFromLevel.setLocation(updatedObject.getValue());
				}
			}
		}
	}

	public long getTime() {
		return time;
	}

	public void setTime(long time) {
		this.time = time;
	}
}
